# import magrittr for piping
library(magrittr)
library(readr)
library(dplyr)
library(plotly)
#read in data to analyze
atp_table <- read_csv("atp_tennis.csv")
Rows: 25362 Columns: 17── Column specification ─────────────────────────────────────────────────────────
Delimiter: ","
chr (9): Tournament, Series, Court, Surface, Round, Player_1, Player_2, Winn...
dbl (7): Best of, Rank_1, Rank_2, Pts_1, Pts_2, Odd_1, Odd_2
date (1): Date
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(atp_table)
NA
#plotly tests
atp_dated <- atp_table %>% filter(Date < "2013-01-01")
plot_ly(atp_dated, x = ~Rank_1, y = ~Rank_2,
text = ~Player_1, type = 'scatter', mode = 'markers',
marker = list(size = ~Odd_2, opacity = 0.5))
player_name = "Djokovic N."
atp_table_swapped <- atp_table %>% rename(Player_1 = Player_2, Player_2 = Player_1)
atp_combined <- rbind(atp_table, atp_table_swapped)
atp_combined <- atp_combined[order(atp_combined$Date),]
opponents <- atp_combined %>%
filter(Player_1 == player_name) %>%
select(Player_2) %>%
table(dnn = "name")
opponents %<>% as.data.frame()
opponents <- opponents[rev(order(opponents$Freq)),]
fig <- plot_ly(opponents, x = ~name, y = ~Freq, type = 'bar')
fig
player_results = list()
players = c("Djokovic N.", "Nadal R.")
for(player in players){
# grab the relevant player ratings
atp_player_result <- atp_combined %>%
filter(Player_1 == player) %>%
# grab names ranks and dates
select(Rank_1 | Rank_2 | Date | Player_1 | Player_2)
# add the player result to the list
player_results[[length(player_results)+1]] = atp_player_result
}
ggplot(bind_rows(player_results, .id="data_frame"),
aes(x=Date, y=Rank_1, group = Player_1)) +
geom_line(aes(color = Player_1)) +
scale_y_reverse() +
labs(y = "Ranking", title = "Player Ranking over Time")

library(ggplot2)
ggplot(head(opponents,10), aes(x=name, y = Freq))

library(shiny)
player_selections <<- data.frame( player =
c(atp_table$Player_1,
atp_table$Player_2)) %>%
# used https://www.rdocumentation.org/packages/base/versions/3.6.2/topics/table
# need the column name to be the same every time
table(dnn = list("name")) %>%
as.data.frame(responseName = "freq")
# https://stackoverflow.com/questions/62716572/selectinput-category-selection
# Stéphane Laurent
# https://shiny.posit.co/r/reference/shiny/latest/updateselectinput
# for context (lets me know onInitialize is running as JavaScript)
onInitialize <- "
function(){
var select = this.$input[0];
this.$dropdown_content.on('mousedown', function(e){
e.preventDefault();
return false;
}).on('click', '.optgroup-header', function(e){
var options = $(this).parent().find('.option');
var items = [];
options.each(function(i, opt){items.push($(opt).data('value'));});
var selections = select.selectize.items;
select.selectize.setValue(items.concat(selections));
});
}
"
shinyApp(
ui = fluidPage(
selectizeInput("state", "Choose a player:",
player_selections,
multiple = TRUE,
options = list(
onInitialize = I(onInitialize)
)
)
),
server = function(input, output){}
)
Listening on http://127.0.0.1:5536
NA
library(GGally)
library(igraph)
player = "Djokovic N."
atp_player_result <- atp_combined %>%
filter(Player_1 == player) %>%
# grab names ranks and dates
select(Player_2)
#make a data frame with the frequency of occurrences
network_table_df <- atp_player_result %>%
table(dnn = list("name")) %>%
as.data.frame(responseName = "freq")
#order by frequency
network_table_df <- network_table_df[order(network_table_df$freq, decreasing = TRUE),]
simple_edge_df <- data.frame(
from = rep(player,length(network_table_df$name)),
to = network_table_df$name,
weight = network_table_df$freq
)
net <- graph_from_data_frame(simple_edge_df)
p <- ggnet2(simplify(net), opponent = to, label = TRUE)
Error in list2(parse = parse, check_overlap = check_overlap, na.rm = na.rm, :
object 'to' not found
simplify(net)
IGRAPH 4cf45fe DNW- 176 175 --
+ attr: name (v/c), weight (e/n)
+ edges from 4cf45fe (vertex names):
[1] Djokovic N.->Nadal R. Djokovic N.->Federer R.
[3] Djokovic N.->Murray A. Djokovic N.->Nishikori K.
[5] Djokovic N.->Berdych T. Djokovic N.->Cilic M.
[7] Djokovic N.->Tsitsipas S. Djokovic N.->Wawrinka S.
[9] Djokovic N.->Medvedev D. Djokovic N.->Thiem D.
[11] Djokovic N.->Kohlschreiber P. Djokovic N.->Raonic M.
[13] Djokovic N.->Del Potro J.M. Djokovic N.->Dimitrov G.
[15] Djokovic N.->Monfils G. Djokovic N.->Bautista Agut R.
+ ... omitted several edges
rep(player,length(atp_player_result$Player_2))
lets see if we can make
#read in data to analyze
old_atp_df <- read_csv("Tennis Data.csv")
head(old_atp_df)
library(dplyr)
#old_tournament_locations <- old_atp_df[!duplicated(old_atp_df$Tournament),]
new_tournaments <- atp_table[!duplicated(atp_table$Tournament), "Tournament"]
#new_tournaments$Tournament %in%old_tournament_locations$Tournament
#old_tournament_locations$Tournament %in% new_tournaments$Tournament
new_tournaments
library(maps)
#map.cities(old_tournament_locations$Location)
world.cities
#old_tournament_locations$Location[which(old_tournament_locations$Location %in% world.cities$name)]
world.cities[which(world.cities$name %in% old_tournament_locations$Location),]
locations <- world.cities[which(world.cities$name %in% old_tournament_locations$Location),]
fig <- plot_geo(locations, lat = ~lat, lon = ~long)
fig <- fig %>% add_markers(
text = ~paste(airport, city, state, paste("Arrivals:", cnt), sep = "<br />"),
color = ~cnt, symbol = I("square"), size = I(8), hoverinfo = "text"
)
fig
result <- merge(old_tournament_locations,
world.cities, by.x = "Location", by.y = "name")
#with(world.data, lat[match(old_tournament_locations$Location, name])
player = "Djokovic N."
tournament = atp_combined$Tournament[1]
atp_player_result <- atp_combined %>%
filter(Player_1 == player & Tournament == tournament) %>%
select(Player_2)
atp_player_result
network_table_df <- atp_player_result %>%
table(dnn = list("name")) %>%
as.data.frame(responseName = "freq")
network_table_df <- network_table_df[order(network_table_df$freq, decreasing = TRUE),]
edge_df <- data.frame(
from = rep(player,length(network_table_df$name)),
to = network_table_df$name,
weight = network_table_df$freq
)
net <- graph_from_data_frame(edge_df)
p <- ggnet2(simplify(net), size = 3, label = TRUE)
p <- ggplotly(p) %>% layout(xaxis = list(visible = FALSE),
yaxis = list(visible = FALSE))
p
NA
LS0tDQp0aXRsZTogIkluZGl2aWR1YWwgUHJvamVjdCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KYXV0aG9yOiBTcGVuY2VyIFNraWRtb3JlDQotLS0NCg0KDQoNCmBgYHtyfQ0KIyBpbXBvcnQgbWFncml0dHIgZm9yIHBpcGluZw0KbGlicmFyeShtYWdyaXR0cikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShwbG90bHkpDQojcmVhZCBpbiBkYXRhIHRvIGFuYWx5emUNCmF0cF90YWJsZSA8LSByZWFkX2NzdigiYXRwX3Rlbm5pcy5jc3YiKQ0KaGVhZChhdHBfdGFibGUpDQoNCmBgYA0KYGBge3J9DQojcGxvdGx5IHRlc3RzDQphdHBfZGF0ZWQgPC0gYXRwX3RhYmxlICU+JSBmaWx0ZXIoRGF0ZSA8ICIyMDEzLTAxLTAxIikNCnBsb3RfbHkoYXRwX2RhdGVkLCB4ID0gflJhbmtfMSwgeSA9IH5SYW5rXzIsIA0KICAgICAgICB0ZXh0ID0gflBsYXllcl8xLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ21hcmtlcnMnLA0KICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSB+T2RkXzIsIG9wYWNpdHkgPSAwLjUpKQ0KYGBgDQoNCmBgYHtyfQ0KcGxheWVyX25hbWUgPSAiRGpva292aWMgTi4iDQphdHBfdGFibGVfc3dhcHBlZCA8LSBhdHBfdGFibGUgJT4lIHJlbmFtZShQbGF5ZXJfMSA9IFBsYXllcl8yLCBQbGF5ZXJfMiA9IFBsYXllcl8xKQ0KYXRwX2NvbWJpbmVkIDwtIHJiaW5kKGF0cF90YWJsZSwgYXRwX3RhYmxlX3N3YXBwZWQpDQphdHBfY29tYmluZWQgPC0gYXRwX2NvbWJpbmVkW29yZGVyKGF0cF9jb21iaW5lZCREYXRlKSxdDQpvcHBvbmVudHMgPC0gYXRwX2NvbWJpbmVkICU+JQ0KICBmaWx0ZXIoUGxheWVyXzEgPT0gcGxheWVyX25hbWUpICU+JQ0KICBzZWxlY3QoUGxheWVyXzIpICU+JQ0KICB0YWJsZShkbm4gPSAibmFtZSIpDQpvcHBvbmVudHMgJTw+JSBhcy5kYXRhLmZyYW1lKCkNCm9wcG9uZW50cyA8LSBvcHBvbmVudHNbcmV2KG9yZGVyKG9wcG9uZW50cyRGcmVxKSksXQ0KZmlnIDwtIHBsb3RfbHkob3Bwb25lbnRzLCB4ID0gfm5hbWUsIHkgPSB+RnJlcSwgdHlwZSA9ICdiYXInKQ0KZmlnDQpgYGANCmBgYHtyfQ0KcGxheWVyX3Jlc3VsdHMgPSBsaXN0KCkNCnBsYXllcnMgPSBjKCJEam9rb3ZpYyBOLiIsICJOYWRhbCBSLiIpDQpmb3IocGxheWVyIGluIHBsYXllcnMpew0KICAjIGdyYWIgdGhlIHJlbGV2YW50IHBsYXllciByYXRpbmdzDQogIGF0cF9wbGF5ZXJfcmVzdWx0IDwtIGF0cF9jb21iaW5lZCAlPiUNCiAgICBmaWx0ZXIoUGxheWVyXzEgPT0gcGxheWVyKSAlPiUNCiAgICAjIGdyYWIgbmFtZXMgcmFua3MgYW5kIGRhdGVzDQogICAgc2VsZWN0KFJhbmtfMSB8IFJhbmtfMiB8IERhdGUgfCBQbGF5ZXJfMSB8IFBsYXllcl8yKQ0KICAjIGFkZCB0aGUgcGxheWVyIHJlc3VsdCB0byB0aGUgbGlzdA0KICBwbGF5ZXJfcmVzdWx0c1tbbGVuZ3RoKHBsYXllcl9yZXN1bHRzKSsxXV0gPSBhdHBfcGxheWVyX3Jlc3VsdA0KfQ0KZ2dwbG90KGJpbmRfcm93cyhwbGF5ZXJfcmVzdWx0cywgLmlkPSJkYXRhX2ZyYW1lIiksDQogICAgICAgYWVzKHg9RGF0ZSwgeT1SYW5rXzEsIGdyb3VwID0gUGxheWVyXzEpKSArDQogIGdlb21fbGluZShhZXMoY29sb3IgPSBQbGF5ZXJfMSkpICsNCiAgc2NhbGVfeV9yZXZlcnNlKCkgKw0KICBsYWJzKHkgPSAiUmFua2luZyIsIHRpdGxlID0gIlBsYXllciBSYW5raW5nIG92ZXIgVGltZSIpDQoNCmBgYA0KDQpgYGB7cn0NCg0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpnZ3Bsb3QoaGVhZChvcHBvbmVudHMsMTApLCBhZXMoeD1uYW1lLCB5ID0gRnJlcSkpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KHNoaW55KQ0KcGxheWVyX3NlbGVjdGlvbnMgPDwtIGRhdGEuZnJhbWUoIHBsYXllciA9IA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYyhhdHBfdGFibGUkUGxheWVyXzEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdHBfdGFibGUkUGxheWVyXzIpKSAlPiUNCiAgIyB1c2VkIGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9iYXNlL3ZlcnNpb25zLzMuNi4yL3RvcGljcy90YWJsZQ0KICAjIG5lZWQgdGhlIGNvbHVtbiBuYW1lIHRvIGJlIHRoZSBzYW1lIGV2ZXJ5IHRpbWUNCiAgdGFibGUoZG5uID0gbGlzdCgibmFtZSIpKSAlPiUNCiAgYXMuZGF0YS5mcmFtZShyZXNwb25zZU5hbWUgPSAiZnJlcSIpDQoNCiMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNjI3MTY1NzIvc2VsZWN0aW5wdXQtY2F0ZWdvcnktc2VsZWN0aW9uDQojIFN0w6lwaGFuZSBMYXVyZW50DQojIGh0dHBzOi8vc2hpbnkucG9zaXQuY28vci9yZWZlcmVuY2Uvc2hpbnkvbGF0ZXN0L3VwZGF0ZXNlbGVjdGlucHV0DQojIGZvciBjb250ZXh0IChsZXRzIG1lIGtub3cgb25Jbml0aWFsaXplIGlzIHJ1bm5pbmcgYXMgSmF2YVNjcmlwdCkNCm9uSW5pdGlhbGl6ZSA8LSAiDQpmdW5jdGlvbigpew0KICB2YXIgc2VsZWN0ID0gdGhpcy4kaW5wdXRbMF07DQogIHRoaXMuJGRyb3Bkb3duX2NvbnRlbnQub24oJ21vdXNlZG93bicsIGZ1bmN0aW9uKGUpew0KICAgIGUucHJldmVudERlZmF1bHQoKTsgDQogICAgcmV0dXJuIGZhbHNlOw0KICB9KS5vbignY2xpY2snLCAnLm9wdGdyb3VwLWhlYWRlcicsIGZ1bmN0aW9uKGUpew0KICAgIHZhciBvcHRpb25zID0gJCh0aGlzKS5wYXJlbnQoKS5maW5kKCcub3B0aW9uJyk7DQogICAgdmFyIGl0ZW1zID0gW107DQogICAgb3B0aW9ucy5lYWNoKGZ1bmN0aW9uKGksIG9wdCl7aXRlbXMucHVzaCgkKG9wdCkuZGF0YSgndmFsdWUnKSk7fSk7DQogICAgdmFyIHNlbGVjdGlvbnMgPSBzZWxlY3Quc2VsZWN0aXplLml0ZW1zOw0KICAgIHNlbGVjdC5zZWxlY3RpemUuc2V0VmFsdWUoaXRlbXMuY29uY2F0KHNlbGVjdGlvbnMpKTsNCiAgfSk7DQp9DQoiDQoNCnNoaW55QXBwKA0KICB1aSA9IGZsdWlkUGFnZSgNCiAgICBzZWxlY3RpemVJbnB1dCgic3RhdGUiLCAiQ2hvb3NlIGEgcGxheWVyOiIsDQogICAgICAgICAgICAgICAgICAgcGxheWVyX3NlbGVjdGlvbnMsDQogICAgICAgICAgICAgICAgICAgbXVsdGlwbGUgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICBvcHRpb25zID0gbGlzdCgNCiAgICAgICAgICAgICAgICAgICAgIG9uSW5pdGlhbGl6ZSA9IEkob25Jbml0aWFsaXplKQ0KICAgICAgICAgICAgICAgICAgICkNCiAgICApDQogICksDQogIHNlcnZlciA9IGZ1bmN0aW9uKGlucHV0LCBvdXRwdXQpe30NCikNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoR0dhbGx5KQ0KbGlicmFyeShpZ3JhcGgpDQpwbGF5ZXIgPSAiRGpva292aWMgTi4iIA0KICAgIGF0cF9wbGF5ZXJfcmVzdWx0IDwtIGF0cF9jb21iaW5lZCAlPiUNCiAgICAgIGZpbHRlcihQbGF5ZXJfMSA9PSBwbGF5ZXIpICU+JQ0KICAgICAgIyBncmFiIG5hbWVzIHJhbmtzIGFuZCBkYXRlcw0KICAgICAgc2VsZWN0KFBsYXllcl8yKQ0KICAgICNtYWtlIGEgZGF0YSBmcmFtZSB3aXRoIHRoZSBmcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZXMNCiAgICBuZXR3b3JrX3RhYmxlX2RmIDwtIGF0cF9wbGF5ZXJfcmVzdWx0ICU+JQ0KICAgICAgdGFibGUoZG5uID0gbGlzdCgibmFtZSIpKSAlPiUNCiAgICAgIGFzLmRhdGEuZnJhbWUocmVzcG9uc2VOYW1lID0gImZyZXEiKQ0KICAgICNvcmRlciBieSBmcmVxdWVuY3kNCiAgICBuZXR3b3JrX3RhYmxlX2RmIDwtIG5ldHdvcmtfdGFibGVfZGZbb3JkZXIobmV0d29ya190YWJsZV9kZiRmcmVxLCBkZWNyZWFzaW5nID0gVFJVRSksXQ0KICAgIHNpbXBsZV9lZGdlX2RmIDwtIGRhdGEuZnJhbWUoDQogICAgICBmcm9tID0gcmVwKHBsYXllcixsZW5ndGgobmV0d29ya190YWJsZV9kZiRuYW1lKSksDQogICAgICB0byA9IG5ldHdvcmtfdGFibGVfZGYkbmFtZSwNCiAgICAgIHdlaWdodCA9IG5ldHdvcmtfdGFibGVfZGYkZnJlcQ0KICAgICkNCiAgICANCiAgICBuZXQgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKHNpbXBsZV9lZGdlX2RmKQ0KICAgIHAgPC0gZ2duZXQyKHNpbXBsaWZ5KG5ldCksIGxhYmVsID0gVFJVRSkNCiAgICBnZ3Bsb3RseShwKSAlPiUgbGF5b3V0KHhheGlzID0gbGlzdCh2aXNpYmxlID0gRkFMU0UpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHZpc2libGUgPSBGQUxTRSkpDQpgYGANCmBgYHtyfQ0Kc2ltcGxpZnkobmV0KQ0KYGBgDQpgYGB7cn0NCnJlcChwbGF5ZXIsbGVuZ3RoKGF0cF9wbGF5ZXJfcmVzdWx0JFBsYXllcl8yKSkNCmBgYA0KbGV0cyBzZWUgaWYgd2UgY2FuIG1ha2UNCmBgYHtyfQ0KI3JlYWQgaW4gZGF0YSB0byBhbmFseXplDQpvbGRfYXRwX2RmIDwtIHJlYWRfY3N2KCJUZW5uaXMgRGF0YS5jc3YiKQ0KaGVhZChvbGRfYXRwX2RmKQ0KYGBgDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQojb2xkX3RvdXJuYW1lbnRfbG9jYXRpb25zIDwtIG9sZF9hdHBfZGZbIWR1cGxpY2F0ZWQob2xkX2F0cF9kZiRUb3VybmFtZW50KSxdDQpuZXdfdG91cm5hbWVudHMgPC0gYXRwX3RhYmxlWyFkdXBsaWNhdGVkKGF0cF90YWJsZSRUb3VybmFtZW50KSwgIlRvdXJuYW1lbnQiXQ0KI25ld190b3VybmFtZW50cyRUb3VybmFtZW50ICVpbiVvbGRfdG91cm5hbWVudF9sb2NhdGlvbnMkVG91cm5hbWVudA0KI29sZF90b3VybmFtZW50X2xvY2F0aW9ucyRUb3VybmFtZW50ICVpbiUgbmV3X3RvdXJuYW1lbnRzJFRvdXJuYW1lbnQNCm5ld190b3VybmFtZW50cw0KYGBgDQpgYGB7cn0NCmxpYnJhcnkobWFwcykNCiNtYXAuY2l0aWVzKG9sZF90b3VybmFtZW50X2xvY2F0aW9ucyRMb2NhdGlvbikNCndvcmxkLmNpdGllcw0KI29sZF90b3VybmFtZW50X2xvY2F0aW9ucyRMb2NhdGlvblt3aGljaChvbGRfdG91cm5hbWVudF9sb2NhdGlvbnMkTG9jYXRpb24gJWluJSB3b3JsZC5jaXRpZXMkbmFtZSldDQoNCndvcmxkLmNpdGllc1t3aGljaCh3b3JsZC5jaXRpZXMkbmFtZSAlaW4lIG9sZF90b3VybmFtZW50X2xvY2F0aW9ucyRMb2NhdGlvbiksXQ0KDQpgYGANCmBgYHtyfQ0KbG9jYXRpb25zIDwtIHdvcmxkLmNpdGllc1t3aGljaCh3b3JsZC5jaXRpZXMkbmFtZSAlaW4lIG9sZF90b3VybmFtZW50X2xvY2F0aW9ucyRMb2NhdGlvbiksXQ0KZmlnIDwtIHBsb3RfZ2VvKGxvY2F0aW9ucywgbGF0ID0gfmxhdCwgbG9uID0gfmxvbmcpDQoNCmZpZyA8LSBmaWcgJT4lIGFkZF9tYXJrZXJzKA0KDQogICAgdGV4dCA9IH5wYXN0ZShhaXJwb3J0LCBjaXR5LCBzdGF0ZSwgcGFzdGUoIkFycml2YWxzOiIsIGNudCksIHNlcCA9ICI8YnIgLz4iKSwNCg0KICAgIGNvbG9yID0gfmNudCwgc3ltYm9sID0gSSgic3F1YXJlIiksIHNpemUgPSBJKDgpLCBob3ZlcmluZm8gPSAidGV4dCINCg0KICApDQpmaWcNCmBgYA0KYGBge3J9DQoNCnJlc3VsdCA8LSBtZXJnZShvbGRfdG91cm5hbWVudF9sb2NhdGlvbnMsDQogICAgICB3b3JsZC5jaXRpZXMsIGJ5LnggPSAiTG9jYXRpb24iLCBieS55ID0gIm5hbWUiKSANCg0KI3dpdGgod29ybGQuZGF0YSwgbGF0W21hdGNoKG9sZF90b3VybmFtZW50X2xvY2F0aW9ucyRMb2NhdGlvbiwgbmFtZV0pDQpgYGANCg0KYGBge3J9DQpwbGF5ZXIgPSAiRGpva292aWMgTi4iDQp0b3VybmFtZW50ID0gYXRwX2NvbWJpbmVkJFRvdXJuYW1lbnRbMV0NCmF0cF9wbGF5ZXJfcmVzdWx0IDwtIGF0cF9jb21iaW5lZCAlPiUNCiAgICAgIGZpbHRlcihQbGF5ZXJfMSA9PSBwbGF5ZXIgJiBUb3VybmFtZW50ID09IHRvdXJuYW1lbnQpICU+JQ0KICAgICAgc2VsZWN0KFBsYXllcl8yKQ0KYXRwX3BsYXllcl9yZXN1bHQNCg0KbmV0d29ya190YWJsZV9kZiA8LSBhdHBfcGxheWVyX3Jlc3VsdCAlPiUNCiAgICAgIHRhYmxlKGRubiA9IGxpc3QoIm5hbWUiKSkgJT4lDQogICAgICBhcy5kYXRhLmZyYW1lKHJlc3BvbnNlTmFtZSA9ICJmcmVxIikNCiAgICBuZXR3b3JrX3RhYmxlX2RmIDwtIG5ldHdvcmtfdGFibGVfZGZbb3JkZXIobmV0d29ya190YWJsZV9kZiRmcmVxLCBkZWNyZWFzaW5nID0gVFJVRSksXQ0KICAgIA0KZWRnZV9kZiA8LSBkYXRhLmZyYW1lKA0KICAgICAgZnJvbSA9IHJlcChwbGF5ZXIsbGVuZ3RoKG5ldHdvcmtfdGFibGVfZGYkbmFtZSkpLA0KICAgICAgdG8gPSBuZXR3b3JrX3RhYmxlX2RmJG5hbWUsDQogICAgICB3ZWlnaHQgPSBuZXR3b3JrX3RhYmxlX2RmJGZyZXENCiAgICApDQoNCm5ldCA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZWRnZV9kZikNCiAgICBwIDwtIGdnbmV0MihzaW1wbGlmeShuZXQpLCBzaXplID0gMywgbGFiZWwgPSBUUlVFKQ0KICAgIHAgPC0gZ2dwbG90bHkocCkgJT4lIGxheW91dCh4YXhpcyA9IGxpc3QodmlzaWJsZSA9IEZBTFNFKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHZpc2libGUgPSBGQUxTRSkpDQogICAgcA0KICAgIA0KYGBgDQoNCg==